import type CreateApp from "./app";
import { scopedCSSCode } from "./scopedCSS";
import {
  fetchSource,
  fillUrl,
  foreachDom,
  isCssInner,
  isCssLink,
  isScriptCode,
  isScriptSrc,
} from "./utils";

// 读取到html字符串 -> 修改 -> 添加到div
export default async function loadHtml(url: string) {
  const div = document.createElement("div");

  let html: string = await fetchSource(url);
  html = parseHtml(html); // 修改 <head/> 和 <body/>
  div.innerHTML = html;

  return div;
}

/**
 <html>
  <head></head>
  <body></body>
 </html>
🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽
 <html>
  <micro-app-head></micro-app-head>
  <micro-app-body></micro-app-body>
 </html>
 */
function parseHtml(html: string) {
  return html
    .replace(/<head[^>]*>[\s\S]*?<\/head>/i, (match) => {
      // 将head标签替换为micro-app-head，因为web页面只允许有一个head标签
      return match
        .replace(/<head/i, "<micro-app-head")
        .replace(/<\/head>/i, "</micro-app-head>");
    })
    .replace(/<body[^>]*>[\s\S]*?<\/body>/i, (match) => {
      // 将body标签替换为micro-app-body，防止与基座应用的body标签重复导致的问题。
      return match
        .replace(/<body/i, "<micro-app-body")
        .replace(/<\/body>/i, "</micro-app-body>");
    });
}

export async function extract(app: CreateApp) {
  const parent = app.source.htmlDom!;
  const tasks: Promise<any>[] = [];

  foreachDom(parent, (dom) => {
    // 处理: <link href='...' />
    // <link/> --> css code --> 隔离 --> <style/>
    if (isCssLink(dom)) tasks.push(link(dom, app));
    // 处理: <style>...</style>
    // css隔离: <style/> --> 隔离
    if (isCssInner(dom)) style(dom, app.name);
    // 处理: <script src='...'></script>
    // 通过src获取code
    if (isScriptSrc(dom)) tasks.push(scriptSrc(dom, app));
    // 处理: <script>...</script>
    // 直接获取code
    if (isScriptCode(dom)) scriptCode(dom, app);
  });

  await Promise.all(tasks);
}

/**
 * 处理: <script>...</script>
 * 储存代码 --> 删除标签
 */
function scriptCode(dom: Element, app: CreateApp) {
  const key = Math.random().toString(36).substring(2, 15);
  const code = dom.textContent!;
  app.source.scripts.set(key, { code, isExternal: false });
  dom.remove();
}

/**
 * 处理: <script src='...'></script>
 */
async function scriptSrc(dom: Element, app: CreateApp) {
  let src = dom.getAttribute("src")!;
  src = fillUrl(src, app.url);
  const code = await fetchSource(src);
  app.source.scripts.set(src, { code, isExternal: true });
  dom.remove();
}

/**
 * 处理: <link href='...' />
 * <link/> --> css code --> 隔离 --> <style/>
 */
async function link(domLink: Element, app: CreateApp) {
  let url = domLink.getAttribute("href");
  if (!url) return;
  url = fillUrl(url, app.url); // 补全url

  let code = await fetchSource(url); // 获取css code
  code = scopedCSSCode(code, app.name); // css隔离
  app.source.links.set(url, { code }); // 保存css code
  domLink.remove(); // 移除掉<link/>

  // 生成<style></style>
  const style = document.createElement("style");
  style.textContent = code;
  // 添加到到<micro-app-head>
  const microAppHead = app.source.htmlDom!.querySelector("micro-app-head");
  microAppHead?.appendChild(style);
}

/**
 * 处理: <style>...</style>
 */
function style(domStyle: Element, appName: string) {
  const cssCode = domStyle.textContent;
  if (!cssCode) return;
  domStyle.textContent = scopedCSSCode(cssCode, appName);
}
